/**
 * Copyright (c) 2021-2022 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef COMPILER_OPTIMIZER_TEMPLATES_IR_DYN_BASE_TYPES_H
#define COMPILER_OPTIMIZER_TEMPLATES_IR_DYN_BASE_TYPES_H

#include "compiler/optimizer/ir/datatype.h"
#include "source_languages.h"

#include <optional>

% Common::plugins.each_value do |plugin_opts|
% next unless plugin_opts["compiler_base_types"]
% next unless plugin_opts["compiler_base_types"]["header_path_implementation_codegen"]
#include "<%= plugin_opts["compiler_base_types"]["header_path_implementation_codegen"] %>"
% end

% Common::plugins.each_value do |plugin_opts|
% next unless plugin_opts["compiler_extensions"]
% next unless plugin_opts["compiler_extensions"]["main_header_path"]
#include "<%= plugin_opts["compiler_extensions"]["main_header_path"] %>"
% end

namespace panda::compiler {
inline AnyBaseType NumericDataTypeToAnyType([[maybe_unused]] panda::compiler::DataType::Type type,
                                            panda::compiler::SourceLanguage language) {
    ASSERT(type == panda::compiler::DataType::Type::UINT8 || type == panda::compiler::DataType::Type::INT8 ||
           type == panda::compiler::DataType::Type::UINT16 || type == panda::compiler::DataType::Type::INT16 ||
           type == panda::compiler::DataType::Type::UINT32 || type == panda::compiler::DataType::Type::INT32 ||
           type == panda::compiler::DataType::Type::UINT64 || type == panda::compiler::DataType::Type::INT64 ||
           type == panda::compiler::DataType::Type::FLOAT32 || type == panda::compiler::DataType::Type::FLOAT64);
    switch (type) {
        case panda::compiler::DataType::Type::INT8:
        case panda::compiler::DataType::Type::UINT8:
        case panda::compiler::DataType::Type::INT16:
        case panda::compiler::DataType::Type::UINT16:
        case panda::compiler::DataType::Type::INT32:
            return panda::compiler::AnyBaseType::ECMASCRIPT_INT_TYPE;
        case panda::compiler::DataType::Type::UINT32:
        case panda::compiler::DataType::Type::INT64:
        case panda::compiler::DataType::Type::UINT64:
        case panda::compiler::DataType::Type::FLOAT32:
            return panda::compiler::AnyBaseType::UNDEFINED_TYPE;
        case panda::compiler::DataType::Type::FLOAT64:
            return panda::compiler::AnyBaseType::ECMASCRIPT_DOUBLE_TYPE;
        default:
            UNREACHABLE();
            return panda::compiler::AnyBaseType::UNDEFINED_TYPE;
    }
}

inline AnyBaseType GetAnyStringType([[maybe_unused]] panda::compiler::SourceLanguage language) {
    return panda::compiler::AnyBaseType::ECMASCRIPT_STRING_TYPE;
}

/*
 * Checks that the exact type of `any` value that is subtype of `type` also can be subtype of `super_type`.
 * Returns `true` if the `type` is equal to or subtype of `super_type` (i.e `type` is `STRING_TYPE` and
 * `super_type` is `OBJECT_TYPE`).
 * Returns `false` if there is no subtype relationship beetween `type` an `super_type` (i.e `type` is `INT_TYPE`
 * and `super_type` is `OBJECT_TYPE`)
 * Return `nullopt` if the `super_type` is subtype of `type` (i.e `type` is `OBJECT` and `super_type` is `STRING`).
 * In this case we need to check exact type at the runtime.
 */
inline std::optional<bool> IsAnyTypeCanBeSubtypeOf([[maybe_unused]] AnyBaseType super_type,
                                                   [[maybe_unused]] AnyBaseType type,
                                                   [[maybe_unused]] panda::compiler::SourceLanguage language) {
    if (super_type == type) {
        return true;
    }

    switch (super_type) {
        case panda::compiler::AnyBaseType::ECMASCRIPT_OBJECT_TYPE:
            switch (type) {
                case panda::compiler::AnyBaseType::ECMASCRIPT_STRING_TYPE:
                    return true;
                default:
                    break;
            }
            break;
        case panda::compiler::AnyBaseType::ECMASCRIPT_STRING_TYPE:
            switch (type) {
                case panda::compiler::AnyBaseType::ECMASCRIPT_OBJECT_TYPE:
                    return std::nullopt;
                default:
                    break;
            }
            break;
        default:
            break;
    }

    return false; 
}

inline panda::compiler::DataType::Type AnyBaseTypeToDataType([[maybe_unused]] AnyBaseType any_type) {
    switch(any_type) {
        case AnyBaseType::UNDEFINED_TYPE:
            return panda::compiler::DataType::Type::ANY;
% Common::plugins.each do |plugin_lang, plugin_opts|
% next unless plugin_opts["compiler_base_types"]
% next unless plugin_opts["compiler_base_types"]["list_types"]
%   has_bool_type = false
%   plugin_opts["compiler_base_types"]["list_types"].each do |type_name, type_pair|
        case AnyBaseType::<%= plugin_lang %>_<%= type_name %>:
            return <%= type_pair %>;
%     if (type_pair.include?("BOOL"))
%       if (has_bool_type)
%         abort 'Compiler does not support two dynamic boolean types(See example in Peephole::VisitIfImm)'
%       end
%       has_bool_type = true
%     end
%   end
% end
        default:
            return panda::compiler::DataType::Type::ANY;
    }

    return panda::compiler::DataType::Type::ANY;
}

inline const char *AnyTypeTypeToString(AnyBaseType any_type)
{
    static constexpr auto COUNT = static_cast<uint32_t>(AnyBaseType::COUNT);
    static constexpr std::array<const char *, COUNT> ANYBASETYPE_NAMES = {
        "UNDEFINED_TYPE",
% Common::plugins.each do |plugin_lang, plugin_opts|
% next unless plugin_opts["compiler_base_types"]
% next unless plugin_opts["compiler_base_types"]["list_types"]
%   plugin_opts["compiler_base_types"]["list_types"].each_key do |type_name|
        "<%= plugin_lang %>_<%= type_name %>",
%   end
% end
    };
    auto idx = static_cast<uint32_t>(any_type);
    ASSERT(idx < COUNT);
    return ANYBASETYPE_NAMES[idx];
}

} // namespace panda::compiler

#endif // COMPILER_OPTIMIZER_TEMPLATES_IR_DYN_BASE_TYPES_H
